(function(){ 'use strict'; const $ = id => document.getElementById(id); const form = $('topsoil-form'); if (!form || !window.SCT) return; const projectDefaults = { 'lawn-leveling': {depth:1, tip:'A 1-inch layer is common for light lawn leveling.'}, 'new-lawn': {depth:4, tip:'New lawns often use 4–6 inches over poor subsoil.'}, 'garden-bed': {depth:6, tip:'Garden and flower beds commonly use about 6–8 inches.'}, 'raised-bed': {depth:12, tip:'Use the actual interior depth of the raised bed.'}, 'custom': {depth:3, tip:'Enter the depth needed for your specific project.'} }; function updateShape(){ const shape = $('shape').value; document.querySelectorAll('.shape-rectangle,.shape-triangle,.shape-circle,.shape-custom').forEach(el => { el.hidden = true; }); document.querySelectorAll(`.shape-${shape}`).forEach(el => { el.hidden = false; }); $('length-label').textContent = shape === 'triangle' ? 'Triangle base' : 'Length'; $('width-label').textContent = shape === 'triangle' ? 'Triangle height' : 'Width'; } function updateDensity(){ $('custom-density-wrap').hidden = $('density').value !== 'custom'; } function updateProject(setDepth){ const item = projectDefaults[$('project-type').value]; $('project-tip').textContent = item.tip; if (setDepth) $('depth').value = item.depth; } function calculate(event){ if (event) event.preventDefault(); SCT.showError($('topsoil-error'), ''); const shape = $('shape').value; const quantity = Math.max(1, SCT.integer($('quantity').value, 1)); const length = SCT.positive($('length').value); const width = SCT.positive($('width').value); const diameter = SCT.positive($('diameter').value); const knownArea = SCT.positive($('known-area').value); const depth = SCT.positive($('depth').value); const waste = SCT.positive($('waste').value); const density = $('density').value === 'custom' ? SCT.positive($('custom-density').value) : Number($('density').value); const bagType = $('bag-type').value; const bagPrice = SCT.positive($('bag-price').value); const yardPrice = SCT.positive($('yard-price').value); const delivery = SCT.positive($('delivery').value); const pickupCapacity = SCT.positive($('pickup-capacity').value, 1); const dumpCapacity = SCT.positive($('dump-capacity').value, 10); let areaSingle = 0; if (shape === 'circle') { if (!diameter) return SCT.showError($('topsoil-error'), 'Enter a positive diameter.'); areaSingle = Math.PI * Math.pow(diameter / 2, 2); } else if (shape === 'custom') { if (!knownArea) return SCT.showError($('topsoil-error'), 'Enter a positive square footage.'); areaSingle = knownArea; } else { if (!length || !width) return SCT.showError($('topsoil-error'), 'Enter positive area dimensions.'); areaSingle = shape === 'triangle' ? (length * width) / 2 : length * width; } if (!depth || !density) return SCT.showError($('topsoil-error'), 'Enter a positive depth and soil density.'); const area = areaSingle * quantity; const baseFeet = area * (depth / 12); const cubicFeet = baseFeet * (1 + waste / 100); const exactYards = cubicFeet / 27; const bulkYards = Math.ceil(exactYards * 4) / 4; const pounds = exactYards * density; const tons = pounds / 2000; let bagVolume = 0; let bagLabel = ''; if (bagType === '40lb') { bagVolume = 40 / (density / 27); bagLabel = '40 lb bags'; } else { bagVolume = Number(bagType); bagLabel = `${bagType} cu ft bags`; } const bags = Math.ceil(cubicFeet / bagVolume); const bagCost = bags * bagPrice; const bulkCost = bulkYards * yardPrice + delivery; const pickupExact = exactYards / pickupCapacity; const dumpExact = exactYards / dumpCapacity; const pickupLoads = Math.ceil(pickupExact); const dumpLoads = Math.ceil(dumpExact); let bestCost = '—'; let guidance = `Plan to buy about ${SCT.format(bulkYards, 2)} cubic yards, or ${SCT.plural(bags, 'bag')} if purchasing ${bagLabel}.`; if (bagPrice && yardPrice) { const useBags = bagCost <= bulkCost; bestCost = SCT.money(Math.min(bagCost, bulkCost)); guidance += ` Based on the entered prices, ${useBags ? 'bagged topsoil' : 'bulk topsoil including delivery'} is less expensive.`; } else if (bagPrice) bestCost = SCT.money(bagCost); else if (yardPrice) bestCost = SCT.money(bulkCost); if (pickupLoads > 1) guidance += ' Verify your vehicle payload before hauling; topsoil is heavy.'; SCT.setText('topsoil-main-result', `${SCT.format(bulkYards, 2)} cubic yards`); SCT.setText('shopping-list', `${SCT.format(tons, 2)} tons estimated, or ${SCT.plural(bags, 'bag')}.`); SCT.setText('cubic-yards', `${SCT.format(bulkYards, 2)} yd³`); SCT.setText('tons-needed', `${SCT.format(tons, 2)} tons`); SCT.setText('bags-needed', SCT.plural(bags, 'bag')); SCT.setText('best-cost', bestCost); SCT.setText('buying-guidance', guidance); SCT.setText('area-result', `${SCT.format(area, 1)} ft²`); SCT.setText('cubic-feet', `${SCT.format(cubicFeet, 1)} ft³`); SCT.setText('exact-yards', `${SCT.format(exactYards, 2)} yd³`); SCT.setText('weight-result', `${SCT.format(pounds, 0)} lb`); SCT.setText('bag-cost', bagPrice ? SCT.money(bagCost) : '—'); SCT.setText('bulk-cost', yardPrice ? SCT.money(bulkCost) : '—'); SCT.setText('pickup-loads', `${SCT.plural(pickupLoads, 'load')} (${SCT.format(pickupExact, 2)} exact)`); SCT.setText('dump-loads', `${SCT.plural(dumpLoads, 'load')} (${SCT.format(dumpExact, 2)} exact)`); } function loadExample(){ const values = { 'project-type':'lawn-leveling', shape:'rectangle', quantity:'1', length:'30', width:'20', diameter:'20', 'known-area':'600', depth:'1', waste:'10', density:'2200', 'custom-density':'2200', 'bag-type':'40lb', 'bag-price':'3.98', 'yard-price':'38', delivery:'65', 'pickup-capacity':'1', 'dump-capacity':'10' }; Object.entries(values).forEach(([id,value]) => { $(id).value = value; }); updateProject(false); updateShape(); updateDensity(); calculate(); } form.addEventListener('submit', calculate); $('shape').addEventListener('change', () => { updateShape(); calculate(); }); $('density').addEventListener('change', () => { updateDensity(); calculate(); }); $('project-type').addEventListener('change', () => { updateProject(true); calculate(); }); $('bag-type').addEventListener('change', calculate); $('example-btn').addEventListener('click', loadExample); $('reset-btn').addEventListener('click', () => SCT.reset(form, () => { updateProject(false); updateShape(); updateDensity(); calculate(); })); $('print-btn').addEventListener('click', SCT.print); updateProject(false); updateShape(); updateDensity(); calculate(); })();